"""
Preprocessor registry for managing and discovering preprocessing methods.

This module provides the PreprocessorRegistry class that handles registration,
discovery, and retrieval of preprocessing methods in the system.
"""

import os
import importlib
import inspect
from typing import Dict, List, Optional
from .base import PreprocessorInterface


class PreprocessorRegistry:
    """
    Registry for managing preprocessing methods.
    
    This class provides a centralized system for registering, discovering,
    and retrieving preprocessing methods. It supports both manual registration
    and automatic discovery of preprocessor implementations.
    """
    
    def __init__(self):
        """Initialize the registry with empty processor storage."""
        self.processors: Dict[int, PreprocessorInterface] = {}
        self._register_default_processors()
    
    def register(self, processor: PreprocessorInterface) -> None:
        """
        Register a preprocessing method.
        
        Args:
            processor (PreprocessorInterface): The preprocessor to register
            
        Raises:
            ValueError: If processor ID is already registered
            TypeError: If processor doesn't implement PreprocessorInterface
        """
        if not isinstance(processor, PreprocessorInterface):
            raise TypeError("Processor must implement PreprocessorInterface")
        
        processor_id = processor.get_id()
        
        if processor_id in self.processors:
            existing_name = self.processors[processor_id].get_name()
            raise ValueError(
                f"Processor ID {processor_id} is already registered "
                f"with name '{existing_name}'"
            )
        
        self.processors[processor_id] = processor
    
    def get_processor(self, processor_id: int) -> Optional[PreprocessorInterface]:
        """
        Get processor by ID.
        
        Args:
            processor_id (int): The ID of the processor to retrieve
            
        Returns:
            Optional[PreprocessorInterface]: The processor if found, None otherwise
        """
        return self.processors.get(processor_id)
    
    def get_available_methods(self) -> List[int]:
        """
        Get list of available processor IDs.
        
        Returns:
            List[int]: Sorted list of available processor IDs
        """
        return sorted(self.processors.keys())
    
    def get_method_names(self) -> Dict[int, str]:
        """
        Get mapping of processor IDs to their names.
        
        Returns:
            Dict[int, str]: Dictionary mapping processor IDs to names
        """
        return {
            processor_id: processor.get_name()
            for processor_id, processor in self.processors.items()
        }
    
    def auto_discover_processors(self) -> None:
        """
        Automatically discover and register processors from implementations directory.
        
        This method scans the preprocessors.implementations package for classes
        that implement PreprocessorInterface and registers them automatically.
        """
        try:
            # Get the implementations package path
            implementations_path = os.path.join(
                os.path.dirname(__file__), 'implementations'
            )
            
            if not os.path.exists(implementations_path):
                return
            
            # Import the implementations package
            implementations_module = importlib.import_module(
                'preprocessors.implementations'
            )
            
            # Scan for Python files in implementations directory
            for filename in os.listdir(implementations_path):
                if filename.endswith('.py') and filename != '__init__.py':
                    module_name = filename[:-3]  # Remove .py extension
                    
                    try:
                        # Import the module
                        module = importlib.import_module(
                            f'preprocessors.implementations.{module_name}'
                        )
                        
                        # Find classes that implement PreprocessorInterface
                        for name, obj in inspect.getmembers(module, inspect.isclass):
                            if (obj != PreprocessorInterface and 
                                issubclass(obj, PreprocessorInterface) and 
                                not inspect.isabstract(obj)):
                                
                                try:
                                    # Instantiate and register the processor
                                    processor_instance = obj()
                                    # Check if processor with this ID is already registered
                                    if processor_instance.get_id() not in self.processors:
                                        self.register(processor_instance)
                                except Exception as e:
                                    # Only show warning for non-duplicate registration errors
                                    if "already registered" not in str(e):
                                        print(f"Warning: Failed to register processor {name}: {e}")
                    
                    except ImportError as e:
                        print(f"Warning: Failed to import module {module_name}: {e}")
        
        except Exception as e:
            print(f"Warning: Auto-discovery failed: {e}")
    
    def _register_default_processors(self) -> None:
        """Register default processors that are always available."""
        # Import and register the NoProcessing processor
        from .implementations.no_processing import NoProcessing
        try:
            no_processing = NoProcessing()
            # Check if processor with this ID is already registered
            if no_processing.get_id() not in self.processors:
                self.register(no_processing)
        except Exception as e:
            print(f"Warning: Failed to register default NoProcessing processor: {e}")
    
    def validate_processor(self, processor: PreprocessorInterface) -> bool:
        """
        Validate that a processor properly implements the interface.
        
        Args:
            processor (PreprocessorInterface): The processor to validate
            
        Returns:
            bool: True if processor is valid, False otherwise
        """
        try:
            # Check if processor implements required methods
            if not hasattr(processor, 'process'):
                return False
            if not hasattr(processor, 'get_name'):
                return False
            if not hasattr(processor, 'get_id'):
                return False
            
            # Check if methods return expected types
            processor_id = processor.get_id()
            processor_name = processor.get_name()
            
            if not isinstance(processor_id, int):
                return False
            if not isinstance(processor_name, str):
                return False
            
            return True
        
        except Exception:
            return False
    
    def get_processor_count(self) -> int:
        """
        Get the total number of registered processors.
        
        Returns:
            int: Number of registered processors
        """
        return len(self.processors)
    
    def clear_registry(self) -> None:
        """
        Clear all registered processors.
        
        Note: This will also remove default processors.
        """
        self.processors.clear()
    
    def __str__(self) -> str:
        """String representation of the registry."""
        if not self.processors:
            return "PreprocessorRegistry (empty)"
        
        processor_list = []
        for processor_id in sorted(self.processors.keys()):
            processor = self.processors[processor_id]
            processor_list.append(f"  {processor_id}: {processor.get_name()}")
        
        return f"PreprocessorRegistry:\n" + "\n".join(processor_list)